package cz.uhk.max.matrix;

import java.util.ArrayList;
import java.util.List;
import cz.uhk.max.matrix.impl.MatrixDaoImpl;

public class TreeMatrix extends Matrix {
	private TreeType treeType = TreeType.LEAF;
	private List<TreeMatrix> matrices = new ArrayList<TreeMatrix>();
	private Boolean equality = false; //means equality for whole system
	private Long equationRow = new Long(-1); //means at which row is equation
	private IMatrixDao dao;

	public String equalityForSystem(){
		
		return (equality==true) ? "solution of system" : "not solution (only " + new Long(equationRow+1) + ". r.)";
	}
	
	public TreeMatrix(Matrix M) {
		super(M);
	}

	public TreeMatrix(Integer rowsCount, Integer colsCount) {
		super(rowsCount, colsCount);
	}
	/**
	 * Constructor create an instance of TreeMatrix. 
	 * Constructor can create next TreeMatrixes if the cycle is not finish.
	 * 
	 * @param A
	 *            Matrix A.
	 * @param B
	 *            Matrix B.
	 * @param indexRow
	 *            The index of row for identify left and right hand to computing.
	 * @param lowerBound
	 * 			  The actual value of lowerBound for computing.
	 * @param cycleIndex
	 * 			  Index of recursion cycle.	
	 * 			         
	 */
	
	public TreeMatrix(TreeMatrix A, TreeMatrix B, TreeMatrix lowerBound) {
		//solution is finding for row with index indexRow
		super(lowerBound); // tree matrix is vector with one column
		this.equationRow = lowerBound.getEquationRow();
		try {
			dao = new MatrixDaoImpl();
			//we will try, if lowerBound is	solution for whole system	
			if (dao.isVectorSolution(A, B, lowerBound)) { // 
				System.out.println("TreeMatrix: Vector lowerBound is solution: ");
				lowerBound.print();
				equality = true;
			} else {
				//we will try if there is some not eliminated row
				if (A.getEliminatedRows() <= A.getRowsCount()) { //if row index is greater, then this the end of recursion
					
				List<TreeMatrix> sols = solutionsWithLowerBoundForOneRow(
					 A, B,lowerBound); //
					
					if (sols != null) {
					matrices.addAll(sols);
					System.out.println("TreeMatrix: Vector leads to other solutions, number " + sols.size());
					lowerBound.print();
					treeType = TreeType.BRANCH;
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * @param lowerBound
	 *            Actual values of given lowerBound.
	 * @param A
	 *            Matrix A.
	 * @param B
	 *            Matrix B.
	 * @param rowIndex
	 * 			  Index o row for computing.
	 * @param cycleIndex		  	           
	 * 	          Index of cycle for computing.
	 * @param completedRows		  	           
	 * 	          Rows, that is now finished.
	 * @return Return list of TreeMatrix with solution for given rowIndex,
	 *         if cycleIndex <= A.getRowCount.
	 */
	public List<TreeMatrix> solutionsWithLowerBoundForOneRow(
			 TreeMatrix A, TreeMatrix B, TreeMatrix lowerBound) {
		//result of this void can be 0-n solutions
		List<TreeMatrix> lowestSolutions = new ArrayList<TreeMatrix>();
		
		//we save init values of lowerBound
		TreeMatrix xInit = new TreeMatrix(lowerBound);
		
		TreeMatrix respA= dao.respectVector(A, lowerBound);
		TreeMatrix respB= dao.respectVector(B, lowerBound);
		
		//get index of row with max of maximum value in row (with respect of lowerBound)
		Long rowIndex = dao.indexOfRowWithMax(respA,respB);
		
		//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		// get rows for computing in this void
		// this rows have to respect lower bound
		//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		Row rowA = respA.getRows().get(Integer.parseInt(rowIndex.toString()));
		Row rowB = respB.getRows().get(Integer.parseInt(rowIndex.toString()));
		
		//with this row will not be necessary to compute next		
		A.getRows().get(Integer.parseInt(rowIndex.toString())).setEliminated(true);
		B.getRows().get(Integer.parseInt(rowIndex.toString())).setEliminated(true);
				
		//get value of left and right side for row with rowIndex
		Col a = dao.multiRowWithVector(rowA, lowerBound);
		Col b = dao.multiRowWithVector(rowB, lowerBound);
		
		//auxiliary matrix, will holds new lower bound if such exists
		TreeMatrix x;
		//auxiliary matrix
		TreeMatrix newMatrix;

		//We try if lowerBound is solution for one row (a = b)
		if (a.getValue().equals(b.getValue())) {
			try {
				//if solution is for one row, then we create new matrix 
				//(at its constructor will be check if it is solution for whole system)
				newMatrix = new TreeMatrix(A, B, lowerBound);
				newMatrix.setEquationRow(rowIndex);
				if (newMatrix != null) {
					lowestSolutions.add(newMatrix);
					}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
			return lowestSolutions;
		} else {
			//lower bound is not solution for this row (a <> b), we will try to find solution
			//for this row
			
			// presumption is a > b:
			if (a.getValue() < b.getValue()) {
				//a is lower b
				//we change a and b
				Col tempc = new Col(a);
				a = new Col(b);
				b = new Col(tempc);
				
				//we will change rows rowA and rowB
				Row tempr = new Row(rowA);
				rowA = new Row(rowB);
				rowB = new Row(tempr);
			}
			
			//we will try to find b(i)>=l and b(i)>=a
			//we use rowB and value a, both respect actual value of lower bound
			for (int i = 0; i < A.getColsCount(); i++) {
				if (rowB.getCols().get(i).getValue() >= lowerBound.getCol(i, 0)
						.getValue()
						&& rowB.getCols().get(i).getValue() >= a.getValue()) {
					
					x = new TreeMatrix(xInit);
					//set value of a to x(i)
					x.setCol(i, 0, a.getValue());
					x.setEquationRow(rowIndex);
					System.out.println("There is some possible candidate:");
					x.print();
					
					//use respected
					lowestSolutions.add(new TreeMatrix(A, B, x));
				}
			}
			
			if (lowestSolutions.size() != 0) {
				return lowestSolutions;
			} else {
				return null;
			}
		}
	}

	public void setMatrices(List<TreeMatrix> matrices) {
		if (matrices != null) {
			treeType = TreeType.BRANCH;
		}
		this.matrices = matrices;
	}
	
	public Long getEliminatedRows(){
		int count = 0;
		for (int i = 0; i < rowsCount; i++) {
			if (rows.get(i).isEliminated() == true) {
				count = count + 1;
			}
		}
		return (long)count;
	}
	
	public List<TreeMatrix> getMatrices() {
		return matrices;
	}

	public void setTreeType(TreeType treeType) {
		this.treeType = treeType;
	}

	public TreeType getTreeType() {
		return treeType;
	}

	public void setEquality(Boolean equality) {
		this.equality = equality;
	}

	public Boolean getEquality() {
		return equality;
	}

	public void setEquationRow(Long equationRow) {
		this.equationRow = equationRow;
	}

	public Long getEquationRow() {
		return equationRow;
	}

}
